/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package io.netty.buffer;

import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;

import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.FastThreadLocalThread;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PooledByteBufAllocator extends AbstractByteBufAllocator implements
    ByteBufAllocatorMetricProvider {

  private static final InternalLogger logger = InternalLoggerFactory
      .getInstance(PooledByteBufAllocator.class);
  private static final int DEFAULT_NUM_HEAP_ARENA;
  private static final int DEFAULT_NUM_DIRECT_ARENA;

  // 8192  2^13，即2的13次方  表示一个页默认的大小 8KB
  private static final int DEFAULT_PAGE_SIZE;
  // 8192 << 11 = 16 MiB 每个Chunk，指定了将默认页大小向左移动的次数，从而得到每个chunk的大小
  private static final int DEFAULT_MAX_ORDER;
  // 指定了tiny chunk的大小，默认为512KB
  private static final int DEFAULT_TINY_CACHE_SIZE;
  private static final int DEFAULT_SMALL_CACHE_SIZE;
  private static final int DEFAULT_NORMAL_CACHE_SIZE;
  private static final int DEFAULT_MAX_CACHED_BUFFER_CAPACITY;
  private static final int DEFAULT_CACHE_TRIM_INTERVAL;
  private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS;
  private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT;
  static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK;

  private static final int MIN_PAGE_SIZE = 4096;
  private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);

  static {
    int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
    Throwable pageSizeFallbackCause = null;
    try {
      validateAndCalculatePageShifts(defaultPageSize);
    } catch (Throwable t) {
      pageSizeFallbackCause = t;
      defaultPageSize = 8192;
    }

    // 8192  2^13，即2的13次方  表示一个页默认的大小 8KB
    DEFAULT_PAGE_SIZE = defaultPageSize;

    int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11);
    Throwable maxOrderFallbackCause = null;
    try {
      validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
    } catch (Throwable t) {
      maxOrderFallbackCause = t;
      defaultMaxOrder = 11;
    }

    // 11
    DEFAULT_MAX_ORDER = defaultMaxOrder;

    // Determine reasonable default for nHeapArena and nDirectArena.
    // Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
    final Runtime runtime = Runtime.getRuntime();

    /*
     * We use 2 * available processors by default to reduce contention as we use 2 * available processors for the
     * number of EventLoops in NIO and EPOLL as well. If we choose a smaller number we will run into hot spots as
     * allocation and de-allocation needs to be synchronized on the PoolArena.
     *
     * See https://github.com/netty/netty/issues/3888.
     */
    final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
    final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
    DEFAULT_NUM_HEAP_ARENA = Math.max(0,
        SystemPropertyUtil.getInt(
            "io.netty.allocator.numHeapArenas",
            (int) Math.min(
                defaultMinNumArena,
                runtime.maxMemory() / defaultChunkSize / 2 / 3)));
    DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
        SystemPropertyUtil.getInt(
            "io.netty.allocator.numDirectArenas",
            (int) Math.min(
                defaultMinNumArena,
                PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));

    // cache sizes
    // 指定了tiny，small和normal的缓存所使用的元素个数
    DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
    DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
    DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);

    // 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
    // 'Scalable memory allocation using jemalloc'
    // 指定了默认最大缓存容量，为32KB
    DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
        "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);

    // the number of threshold of allocations when cached entries will be freed up if not frequently used
    // 指定了进行trim()操作的次数控制范围，默认为8192次内存申请之后就进行一次trim()操作
    DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
        "io.netty.allocator.cacheTrimInterval", 8192);

    // 指定是否对所有的线程都使用缓存
    DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
        "io.netty.allocator.useCacheForAllThreads", true);

    DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt(
        "io.netty.allocator.directMemoryCacheAlignment", 0);

    // Use 1023 by default as we use an ArrayDeque as backing storage which will then allocate an internal array
    // of 1024 elements. Otherwise we would allocate 2048 and only use 1024 which is wasteful.
    DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt(
        "io.netty.allocator.maxCachedByteBuffersPerChunk", 1023);

    if (logger.isDebugEnabled()) {
      logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
      logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
      if (pageSizeFallbackCause == null) {
        logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
      } else {
        logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
      }
      if (maxOrderFallbackCause == null) {
        logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
      } else {
        logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
      }
      logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
      logger.debug("-Dio.netty.allocator.tinyCacheSize: {}", DEFAULT_TINY_CACHE_SIZE);
      logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
      logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
      logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}",
          DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
      logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
      logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}",
          DEFAULT_USE_CACHE_FOR_ALL_THREADS);
      logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}",
          DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK);
    }
  }

  // 这个属性是供给ByteBufUtil使用的，在这里并没有使用
  public static final PooledByteBufAllocator DEFAULT =
      new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());

  private final PoolArena<byte[]>[] heapArenas;
  private final PoolArena<ByteBuffer>[] directArenas;
  private final int tinyCacheSize;
  private final int smallCacheSize;
  private final int normalCacheSize;
  private final List<PoolArenaMetric> heapArenaMetrics;
  private final List<PoolArenaMetric> directArenaMetrics;
  private final PoolThreadLocalCache threadCache;
  private final int chunkSize;
  private final PooledByteBufAllocatorMetric metric;

  public PooledByteBufAllocator() {
    this(false);
  }

  @SuppressWarnings("deprecation")
  public PooledByteBufAllocator(boolean preferDirect) {
    this(preferDirect, DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA, DEFAULT_PAGE_SIZE,
        DEFAULT_MAX_ORDER);
  }

  @SuppressWarnings("deprecation")
  public PooledByteBufAllocator(int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
    this(false, nHeapArena, nDirectArena, pageSize, maxOrder);
  }

  /**
   * @deprecated use {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int,
   * int, int, int, int, boolean)}
   */
  @Deprecated
  public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena,
      int pageSize, int maxOrder) {
    this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
        DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
  }

  /**
   * @deprecated use {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int,
   * int, int, int, int, boolean)}
   */
  @Deprecated
  public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena,
      int pageSize, int maxOrder,
      int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
    this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, tinyCacheSize, smallCacheSize,
        normalCacheSize, DEFAULT_USE_CACHE_FOR_ALL_THREADS, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
  }

  public PooledByteBufAllocator(boolean preferDirect, int nHeapArena,
      int nDirectArena, int pageSize, int maxOrder, int tinyCacheSize,
      int smallCacheSize, int normalCacheSize,
      boolean useCacheForAllThreads) {
    this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
        tinyCacheSize, smallCacheSize, normalCacheSize,
        useCacheForAllThreads, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
  }

  public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena,
      int pageSize, int maxOrder,
      int tinyCacheSize, int smallCacheSize, int normalCacheSize,
      boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
    super(preferDirect);
    threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
    this.tinyCacheSize = tinyCacheSize; // 512，指定了每个线程中PoolThreadCache中tiny类型的每个SubPageMemoryRegionCache中维护的队列大小
    this.smallCacheSize = smallCacheSize;   // 256，指定了每个线程中PoolThreadCache中tiny类型的每个SubPageMemoryRegionCache中维护的队列大小
    this.normalCacheSize = normalCacheSize; // 64，指定了每个线程中PoolThreadCache中tiny类型的每个NormalMemoryRegionCache中维护的队列大小
    // pageSize=2^13，即8KB  maxOrder=11  计算得到的chunkSize=16M
    // 这里maxOrder的作用是指定了chunk中二叉树的层数，因为其是一个完全二叉树，并且每个叶子节点的大小
    // 都是pageSize，也就是说，整颗二叉树所占用的内存大小就是pageSize不断的乘以2，一直乘以maxOrder=11次，
    // 这也就是这里为什么通过这种while的方式来计算chunkSize的大小的原因
    chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);  // 16MB

    checkPositiveOrZero(nHeapArena, "nHeapArena");  // 24
    checkPositiveOrZero(nDirectArena, "nDirectArena");  // 24

    checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");  // 0
    if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
      throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
    }

    // 这里可以保证directMemoryCacheAlignment是2的指数次方
    if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
      throw new IllegalArgumentException("directMemoryCacheAlignment: "
          + directMemoryCacheAlignment + " (expected: power of two)");
    }

    // 这里主要是对pageSize参数进行校验，保证：①pageSize至少在4KB以上；②pageSize是2的指数次方的一个数
    int pageShifts = validateAndCalculatePageShifts(pageSize);  // pageSize=8192=2^13  pageShifts=13

    // 这里主要是创建了一个PoolArena.HeapArena的数组heapArenas
    // 这里主要是创建了一个HeapArena的数组，元素的个数与CPU的数目有关，本机是24个。
    // 这里每个HeapArena主要由如下部分组成：
    // PoolSubpage<T>[] tinySubpagePools:
    //       维护了tiny类型的缓存池，这是一个数组类型，数组元素个数为32，这里每一个PoolSubpage都是一个链表
    // PoolSubpage<T>[] smallSubpagePools:
    //       维护了small类型的缓存池，这是一个数组类型，数组元素个数为4，这里每一个PoolSubpage都是一个链表
    // q000->q025->q050->q075->q100    qInit->qInit:
    //       维护了chunk类型的缓存列表，这是一个双向链表，链表的每个节点元素内部也还是一个链表
    if (nHeapArena > 0) {
      heapArenas = newArenaArray(nHeapArena);
      List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(heapArenas.length);
      for (int i = 0; i < heapArenas.length; i++) {
        PoolArena.HeapArena arena = new PoolArena.HeapArena(this,
            pageSize, maxOrder, pageShifts, chunkSize,
            directMemoryCacheAlignment);
        heapArenas[i] = arena;
        metrics.add(arena);
      }
      heapArenaMetrics = Collections.unmodifiableList(metrics);
    } else {
      heapArenas = null;
      heapArenaMetrics = Collections.emptyList();
    }

    // 这里主要是创建了一个PoolArena.DirectArena的数组directArenas
    // 这里的DirectArena与上面的HeapArena一致，只不过HeapArena保存的是字节数组，则DirectArena保存的是ByteBuffer
    if (nDirectArena > 0) {
      directArenas = newArenaArray(nDirectArena);
      List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
      for (int i = 0; i < directArenas.length; i++) {
        PoolArena.DirectArena arena = new PoolArena.DirectArena(
            this, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
        directArenas[i] = arena;
        metrics.add(arena);
      }
      directArenaMetrics = Collections.unmodifiableList(metrics);
    } else {
      directArenas = null;
      directArenaMetrics = Collections.emptyList();
    }
    metric = new PooledByteBufAllocatorMetric(this);
  }

  @SuppressWarnings("unchecked")
  private static <T> PoolArena<T>[] newArenaArray(int size) {
    return new PoolArena[size];
  }

  private static int validateAndCalculatePageShifts(int pageSize) {
    if (pageSize < MIN_PAGE_SIZE) {
      throw new IllegalArgumentException(
          "pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + ")");
    }

    if ((pageSize & pageSize - 1) != 0) {
      throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: power of 2)");
    }

    // Logarithm base 2. At this point we know that pageSize is a power of two.
    return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(pageSize);
  }

  private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
    if (maxOrder > 14) {
      throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
    }

    // Ensure the resulting chunkSize does not overflow.
    int chunkSize = pageSize;
    for (int i = maxOrder; i > 0; i--) {
      if (chunkSize > MAX_CHUNK_SIZE / 2) {
        throw new IllegalArgumentException(String.format(
            "pageSize (%d) << maxOrder (%d) must not exceed %d", pageSize, maxOrder,
            MAX_CHUNK_SIZE));
      }
      chunkSize <<= 1;
    }
    return chunkSize;
  }

  @Override
  protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
    PoolThreadCache cache = threadCache.get();
    PoolArena<byte[]> heapArena = cache.heapArena;

    final ByteBuf buf;
    if (heapArena != null) {
      buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
    } else {
      buf = PlatformDependent.hasUnsafe() ?
          new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
          new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

    return toLeakAwareBuffer(buf);
  }

  @Override
  protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
    // 这里主要是从ThreadLocal中获取一个PoolThreadCache对象，初始时该对象是空的，但是会调用其initialize()方法
    // 创建一个新的PoolThreadCache对象
    PoolThreadCache cache = threadCache.get();
    // 由于这里的方法是newDirectBuffer()，因而会使用cache.directArena来进行内存处理
    PoolArena<ByteBuffer> directArena = cache.directArena;

    final ByteBuf buf;
    // 这里directArena默认不是null，因而走if分支，如果获取到的directArena是null，那么就会创建一个非缓存池类型的ByteBuf
    if (directArena != null) {
      buf = directArena.allocate(cache, initialCapacity, maxCapacity);
    } else {
      buf = PlatformDependent.hasUnsafe() ?
          UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
          new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
    }

    return toLeakAwareBuffer(buf);
  }

  /**
   * Default number of heap arenas - System Property: io.netty.allocator.numHeapArenas - default 2 *
   * cores
   */
  public static int defaultNumHeapArena() {
    return DEFAULT_NUM_HEAP_ARENA;
  }

  /**
   * Default number of direct arenas - System Property: io.netty.allocator.numDirectArenas - default
   * 2 * cores
   */
  public static int defaultNumDirectArena() {
    return DEFAULT_NUM_DIRECT_ARENA;
  }

  /**
   * Default buffer page size - System Property: io.netty.allocator.pageSize - default 8192
   */
  public static int defaultPageSize() {
    return DEFAULT_PAGE_SIZE;
  }

  /**
   * Default maximum order - System Property: io.netty.allocator.maxOrder - default 11
   */
  public static int defaultMaxOrder() {
    return DEFAULT_MAX_ORDER;
  }

  /**
   * Default thread caching behavior - System Property: io.netty.allocator.useCacheForAllThreads -
   * default true
   */
  public static boolean defaultUseCacheForAllThreads() {
    return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
  }

  /**
   * Default prefer direct - System Property: io.netty.noPreferDirect - default false
   */
  public static boolean defaultPreferDirect() {
    return PlatformDependent.directBufferPreferred();
  }

  /**
   * Default tiny cache size - System Property: io.netty.allocator.tinyCacheSize - default 512
   */
  public static int defaultTinyCacheSize() {
    return DEFAULT_TINY_CACHE_SIZE;
  }

  /**
   * Default small cache size - System Property: io.netty.allocator.smallCacheSize - default 256
   */
  public static int defaultSmallCacheSize() {
    return DEFAULT_SMALL_CACHE_SIZE;
  }

  /**
   * Default normal cache size - System Property: io.netty.allocator.normalCacheSize - default 64
   */
  public static int defaultNormalCacheSize() {
    return DEFAULT_NORMAL_CACHE_SIZE;
  }

  /**
   * Return {@code true} if direct memory cache alignment is supported, {@code false} otherwise.
   */
  public static boolean isDirectMemoryCacheAlignmentSupported() {
    return PlatformDependent.hasUnsafe();
  }

  @Override
  public boolean isDirectBufferPooled() {
    return directArenas != null;
  }

  /**
   * Returns {@code true} if the calling {@link Thread} has a {@link ThreadLocal} cache for the
   * allocated buffers.
   */
  @Deprecated
  public boolean hasThreadLocalCache() {
    return threadCache.isSet();
  }

  /**
   * Free all cached buffers for the calling {@link Thread}.
   */
  @Deprecated
  public void freeThreadLocalCache() {
    threadCache.remove();
  }

  final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {

    private final boolean useCacheForAllThreads;

    PoolThreadLocalCache(boolean useCacheForAllThreads) {
      this.useCacheForAllThreads = useCacheForAllThreads;
    }

    /**
     * 这里PoolThreadLocalCache的作用类似于ThreadLocal，其可以保存一个与当前线程绑定的缓存数据， 这里的initialValue()方法的作用是，在通过PoolThreadLocalCache方法获取目标对象时，
     * 如果得到的值为null，那么就江南initialValue()方法初始化的值返回
     */
    @Override
    protected synchronized PoolThreadCache initialValue() {
      // 这里leastUsedArena()就是获取对应的PoolArena数组中最少被使用的那个Arena，将其返回。
      // 这里的判断方式是通过比较PoolArena.numThreadCaches属性来进行的，该属性记录了当前PoolArena被
      // 多少个线程所占用了。这里采用的思想就是，找到最少被使用的那个PoolArena，将其存入新的线程缓存中
      final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
      final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);

      Thread current = Thread.currentThread();
      // 只有在指定了为每个线程使用缓存，或者当前线程是FastThreadLocalThread的子类型时，才会使用线程缓存
      if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
        // 这里创建的PoolThreadCache从概念上就是当前线程的内存缓存封装类，其内部主要封装了如下几个参数：
        // MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches：用于存储tiny类型的直接内存缓存，数组长度为32，每个MemoryRegionCache内部维护了一个Queue<Entry<ByteBuffer>>，该队列的长度默认为512
        // MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches：用于存储small类型的直接内存缓存，数组长度为4，每个MemoryRegionCache内部维护了一个Queue<Entry<ByteBuffer>>，该队列的长度默认为256
        // MemoryRegionCache<ByteBuffer>[] normalDirectCaches：用于存储normal类型的直接内存，数组长度为3，每个MemoryRegionCache内部维护了一个Queue<Entry<ByteBuffer>>，该队列的长度为64

        // MemoryRegionCache<byte[]>[] tinySubPageDirectCaches：用于存储tiny类型的堆内存缓存，数组长度为32，每个MemoryRegionCache内部维护了一个Queue<Entry<byte[]>>，该队列的长度默认为512
        // MemoryRegionCache<byte[]>[] smallSubPageDirectCaches：用于存储small类型的堆内存缓存，数组长度为4，每个MemoryRegionCache内部维护了一个Queue<Entry<byte[]>>，该队列的长度默认为256
        // MemoryRegionCache<byte[]>[] normalDirectCaches：用于存储normal类型的堆内存，数组长度为3，每个MemoryRegionCache内部维护了一个Queue<Entry<byte[]>>，该队列的长度为64
        return new PoolThreadCache(
            heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
            DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
      }
      // No caching so just use 0 as sizes.
      return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);
    }

    @Override
    protected void onRemoval(PoolThreadCache threadCache) {
      threadCache.free();
    }

    /**
     * 这里leastUsedArena()方法的作用是找到PoolArena中使用次数最少的一个arena，将其返回
     */
    private <T> PoolArena<T> leastUsedArena(PoolArena<T>[] arenas) {
      if (arenas == null || arenas.length == 0) {
        return null;
      }

      PoolArena<T> minArena = arenas[0];
      for (int i = 1; i < arenas.length; i++) {
        PoolArena<T> arena = arenas[i];
        if (arena.numThreadCaches.get() < minArena.numThreadCaches.get()) {
          minArena = arena;
        }
      }

      return minArena;
    }
  }

  @Override
  public PooledByteBufAllocatorMetric metric() {
    return metric;
  }

  /**
   * Return the number of heap arenas.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#numHeapArenas()}.
   */
  @Deprecated
  public int numHeapArenas() {
    return heapArenaMetrics.size();
  }

  /**
   * Return the number of direct arenas.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#numDirectArenas()}.
   */
  @Deprecated
  public int numDirectArenas() {
    return directArenaMetrics.size();
  }

  /**
   * Return a {@link List} of all heap {@link PoolArenaMetric}s that are provided by this pool.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#heapArenas()}.
   */
  @Deprecated
  public List<PoolArenaMetric> heapArenas() {
    return heapArenaMetrics;
  }

  /**
   * Return a {@link List} of all direct {@link PoolArenaMetric}s that are provided by this pool.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#directArenas()}.
   */
  @Deprecated
  public List<PoolArenaMetric> directArenas() {
    return directArenaMetrics;
  }

  /**
   * Return the number of thread local caches used by this {@link PooledByteBufAllocator}.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#numThreadLocalCaches()}.
   */
  @Deprecated
  public int numThreadLocalCaches() {
    PoolArena<?>[] arenas = heapArenas != null ? heapArenas : directArenas;
    if (arenas == null) {
      return 0;
    }

    int total = 0;
    for (PoolArena<?> arena : arenas) {
      total += arena.numThreadCaches.get();
    }

    return total;
  }

  /**
   * Return the size of the tiny cache.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#tinyCacheSize()}.
   */
  @Deprecated
  public int tinyCacheSize() {
    return tinyCacheSize;
  }

  /**
   * Return the size of the small cache.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#smallCacheSize()}.
   */
  @Deprecated
  public int smallCacheSize() {
    return smallCacheSize;
  }

  /**
   * Return the size of the normal cache.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#normalCacheSize()}.
   */
  @Deprecated
  public int normalCacheSize() {
    return normalCacheSize;
  }

  /**
   * Return the chunk size for an arena.
   *
   * @deprecated use {@link PooledByteBufAllocatorMetric#chunkSize()}.
   */
  @Deprecated
  public final int chunkSize() {
    return chunkSize;
  }

  final long usedHeapMemory() {
    return usedMemory(heapArenas);
  }

  final long usedDirectMemory() {
    return usedMemory(directArenas);
  }

  private static long usedMemory(PoolArena<?>[] arenas) {
    if (arenas == null) {
      return -1;
    }
    long used = 0;
    for (PoolArena<?> arena : arenas) {
      used += arena.numActiveBytes();
      if (used < 0) {
        return Long.MAX_VALUE;
      }
    }
    return used;
  }

  final PoolThreadCache threadCache() {
    PoolThreadCache cache = threadCache.get();
    assert cache != null;
    return cache;
  }

  /**
   * Returns the status of the allocator (which contains all metrics) as string. Be aware this may
   * be expensive and so should not called too frequently.
   */
  public String dumpStats() {
    int heapArenasLen = heapArenas == null ? 0 : heapArenas.length;
    StringBuilder buf = new StringBuilder(512)
        .append(heapArenasLen)
        .append(" heap arena(s):")
        .append(StringUtil.NEWLINE);
    if (heapArenasLen > 0) {
      for (PoolArena<byte[]> a : heapArenas) {
        buf.append(a);
      }
    }

    int directArenasLen = directArenas == null ? 0 : directArenas.length;

    buf.append(directArenasLen)
        .append(" direct arena(s):")
        .append(StringUtil.NEWLINE);
    if (directArenasLen > 0) {
      for (PoolArena<ByteBuffer> a : directArenas) {
        buf.append(a);
      }
    }

    return buf.toString();
  }
}
